iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0

🌟 前提摘要:PHP 基本知識點補充


進入控制器後,有些地方需要一些 PHP 的基本知識點會比較清楚知道程式碼是什麼,畢竟 Laravel 的底層語言就是 PHP

變數 $
$ 在 PHP 中只是變數的前綴,所有變數都必須以 $ 開始;和 JavaScript 的變數宣告方式不同,但變數的基本概念是一樣的。

$variable = 'Hello, World!';

單引號 '...' vs 雙引號 "..."
在 PHP 中,單引號和雙引號用來定義字串,但它們有一些不同:
單引號 '...': 字串內容會被當作很單純的字串處理,不會解析變數或特殊字符。
雙引號 "...": 會解析變數和特殊字符(如 \n 代表換行),有點像 ES6 的樣版自面子。

$name = 'kuku';
$single = 'Hello, $name';      // Hello, $name
$double = "Hello, $name";      // Hello, kuku

陣列 []
在 PHP 中,陣列可以用來存儲多個值,可以是索引陣列或關聯陣列。

// 索引陣列 -> 跟 JavaScript 的陣列很相似用數字索引
$colors = array('Red', 'Green', 'Blue');

// 關聯陣列 -> 跟 JavaScript 的物件很相似使用 key/ value
$person = array('first_name' => 'ting', 'last_name' => 'ku');

依賴注入
依賴注入是一種設計模式,用來管理對象之間的依賴關係。
在 PHP 中,特別是在 Laravel 框架中,這通常通過服務容器(Service Container)來實現。

class UserController extends Controller
{
    protected $userService;

    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }
}

創建和使用控制器


控制器是一種組織應用邏輯的方式,它將請求處理邏輯從路由中抽離,讓代碼更具可維護性和結構化。
控制器通常包含多個方法,每個方法負責處理不同的請求或功能。

創建控制器
使用 Artisan 命令 php artisan make:controller 控制器的檔案名字 來創建控制器,例如:創建一個名為 UserController 的控制器,下指令: php artisan make:controller UserController,就會在 app/Http/Controllers 目錄下生成 UserController.php 文件。

使用控制器

<?php

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;


class UserController extends Controller
{
    /**
     * 顯示所有用戶列表的方法
     *
     * @return string
     */
    public function index(): string
    {
        return '我是 UserController 的 index 方法';
    }

    /**
     * 顯示用於建立使用者的表單
     *
     * @param int|null $id
     * @return string
     */
    public function show(int $id = null): string
    {
        return "Showing item with ID: {$id}";
    }

    /**
     * 把新建立的使用者資料儲存在資料庫中
     *
     * @param Request $request
     * @return JsonResponse
     */
    public function store(Request $request): JsonResponse
    {
        // 返回一個 JSON 響應,顯示提交的數據
        return response()->json([
            'message' => 'Data stored successfully',
            'data'    => $request->all()
        ]);
    }

    /**
     * 更新儲存中的指定使用者
     *
     * @param Request $request
     * @param $id
     * @return JsonResponse
     */
    public function update(Request $request, $id): JsonResponse
    {
        // 返回一個 JSON 響應,顯示更新的數據
        return response()->json([
            'message' => 'Data updated successfully',
            'id'      => $id,
            'data'    => $request->all()
        ]);
    }

    /**
     * 從儲存中刪除指定的使用者
     *
     * @param int|null $id
     * @return string
     */
    public function destroy(int $id = null): string
    {
        // 返回一個文本響應,確認刪除操作
        return "Deleted item with ID: {$id}";
    }
}

Controller 只有一個行為寫法 __invoke()
如果建立的控制器只有使用單一方法(Single Action Controllers),可以使用 __invoke() 方法。

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class ShowProfileController extends Controller
{
    public function __invoke($userId)
    {
        $user = User::findOrFail($userId);
        return view('user.profile', ['user' => $user]);
    }
}

Route 裡面也只要寫上 Controller 的名字就可以抓到 Controller 的資料。

use App\Http\Controllers\ShowProfileController;

Route::get('/user/{userId}', ShowProfileController::class);

Controller middleware用法
第二天有提到請求的生命週期:Request(打球) -> index.php(找球員) -> Kernel(賽程表) -> Router(踢球) -> Middleware(守門員) -> Controller(進網得分);
但是在實例化的先後順序是:Router(球) -> Controller(網子) -> Middleware(守門的人);所以在控制器中使用 middleware,要特別留意實例化順序。

  1. 使用 __construct() 建構函式指定中間件

     class UserController extends Controller
     {
         /**
          * Instantiate a new controller instance.
          *
          * @return void
          */
         public function __construct()
         {
             $this->middleware('auth');
    
             // only:只包含指定的動作,也就是只做 'index'
             $this->middleware('log')->only('index');
    
             // except:排除指定的動作,也就是除了 'store' 其他都不做
             $this->middleware('subscribed')->except('store');
         }
     }
    

    🔔 Controller 抓不到 Middleware 在 __construct() 中建立的快取資料,因為 Controller 會比 Middleware 早實例化。
    🔔 如果畫面或是 postman 噴錯提示有關實例化,可以看相關檔案的 __construct() 生命週期。

  2. 使用閉包註冊 middleware

    $this->middleware(function ($request, $next) {
        // ...
        return $next($request);
    });
    

控制器方法與路由的關聯


在路由文件 routes/api.php 中,可以將路由與控制器方法關聯,目前累積有 3 種寫法:

  1. 關聯寫法

       Route::get('/user', [UserController::class, 'index']);
       Route::get('/user/{id}', [UserController::class, 'show']);
       Route::post('/user', [UserController::class, 'store']);
       Route::put('/user/{id}', [UserController::class, 'update']);
       Route::delete('/user/{id}', [UserController::class, 'destroy']);
    

    在 postman 試打註冊的路由,理論上可以得到以下結論:
    GET /user 路由 -> '我是 UserController 的 index 方法'。
    GET /user 路由
    GET /user/{id} -> 可以看到變數 $user 以及 'Showing item with ID: {id}'。
    GET /user/{id}
    POST /user -> 數據的 JSON 響應。
    POST /user
    PUT /user/{id} -> 更新數據的 JSON 響應。
    PUT /user/{id}
    DELETE /user/{id} -> Deleted item with ID: {id} 。
    DELETE /user/{id}

  2. 之前版本的寫法
    @ 的字串寫法,也可以使用 namespace 的方法給控制器位置,這是舊版本的寫法。

    Route::get('/user', '\App\Http\Controllers\UserController@index');
    
  3. 抽共用的寫法
    把共用的內容都抽到最外面,雖然可以整理的很乾淨,但是 IDE 無法支援立即跳轉到對應方法,所以鮮少使用。

    use App\Http\Controllers\UserController;
    use Illuminate\Support\Facades\Route;
    
    Route::controller(UserController::class)->prefix('user')->group(function () {
        Route::get('/', 'index');
        Route::get('/{id}', 'show');
        Route::post('/', 'store');
        Route::put('/{id}', 'update');
        Route::delete('/{id}', 'destroy');
    });
    

資源控制器(Resource Controllers)


資源控制器(Resource Controllers)是 Laravel 提供的一種便捷方式,用來處理具有 CRUD(Create, Read, Update, Delete)操作的路由和控制器邏輯。使用資源控制器,可以簡化對常見操作的處理,並自動生成相應的路由和方法。

創建資源控制器
下指令 php artisan make:controller 資源控制器檔案名稱 --resource,套件就會自動創建一個包括基本 CRUD 方法的控制器,例如 index、create、store、show、edit、update 和 destroy 方法。

註冊資源路由
使用 Route::resource 就可以註冊資源控制器的路由,而且這個單一的路由可以創建了多個路由來處理資源上的各種行為。

// api.php 中註冊資源路由
Route::resource('posts', PostController::class);

可以透過指令 php artisan route:list 快速了解應用程式,確認目前生成的 api 有哪幾支
可以透過指令 php artisan route:list 快速了解應用程式

每支路由都會有自己對應的控制器方法,以下是對照表,這樣就可以在控制器中找到對應的方法,把想要請他執行的邏輯寫在裡面

Verb URI Action Route Name
GET /post index post.index
GET /post/create create post.create
POST /post store post.store
GET /post/{post} show post.show
GET /post/{post}/edit edit post.edit
PUT/PATCH /post/{post} update post.update
DELETE /post/{post} destory post.destory

資源控制器範例

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    // 對應的 URI GET -> /items
    public function index()
    {
        $posts = Post::all();
        return view('posts.index', compact('posts'));
    }

    // 對應的 URI GET -> /items/create
    public function create()
    {
        return view('posts.create');
    }

    // 對應的 URI POST -> /items
    public function store(Request $request)
    {
        Post::create($request->all());
        return redirect()->route('posts.index');
    }

    // 對應的 URI GET -> /items/{id}
    public function show(Post $post)
    {
        return view('posts.show', compact('post'));
    }

    // 對應的 URI GET -> /items/{id}/edit
    public function edit(Post $post)
    {
        return view('posts.edit', compact('post'));
    }

    // 對應的 URI PUT/PATCH -> /items/{id}
    public function update(Request $request, Post $post)
    {
        $post->update($request->all());
        return redirect()->route('posts.index');
    }

    // 對應的 URI DELETE -> /items/{id}
    public function destroy(Post $post)
    {
        $post->delete();
        return redirect()->route('posts.index');
    }
}

✍🏻 每日任務


延續前一天任務,建立一個控制器並且 dd 出前端帶給後端的會員帳號(暫定 $account)

  1. 用指令 php artisan make:controller UserController 建立控制器,把前一天路由閉包的內容搬到控制器各個方法中

    <?php
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\JsonResponse;
    use Illuminate\Http\Request;
    
    
       class UserController extends Controller
    {
        /**
         * 建立使用者資訊到資料庫
         *
         * @param Request $request
         * @return string
         */
        public function createUserInformation(Request $request): string
        {
            return 'register_success';
        }
    
        /**
         * 處理登入驗證(規劃 - 成功跳轉頁面,失敗轉回登入頁面)
         *
         * @param Request $request
         * @return string
         */
        public function handelLogin(Request $request): string
        {
            $name = $request->input('account');
    
            return "{$name}_login_success";
        }
    
        /**
         * 處理忘記密碼流程
         *
         * @param Request $request
         * @param $account
         * @return string
         */
        public function handelForgot(Request $request, $account = null): string
        {
            damp($account);
    
            return "{$account}_forget";
        }
    }
    
  2. routes/api.php 把路由的閉包修改成控制器方法與路由的關聯

    <?php
    
    use App\Http\Controllers\UserController;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Route;
    
    /*
    |--------------------------------------------------------------------------
    | API Routes
    |--------------------------------------------------------------------------
    |
    | Here is where you can register API routes for your application. These
    | routes are loaded by the RouteServiceProvider and all of them will
    | be assigned to the "api" middleware group. Make something great!
    |
    */
    
    // 在這個組中的路由都會應用註冊、登入、忘記密碼
    Route::prefix('auth')->group(function () {
        Route::post('/register', [UserController::class, 'createUserInformation'])
            ->name('auth.register');
        Route::post('/login', [UserController::class, 'handelLogin'])
            ->name('auth.login');
        Route::get('/forget/{account?}', [UserController::class, 'handelForgot'])
            ->whereAlphaNumeric('account')
            ->name('auth.forget');
    });
    

上一篇
第 3 天:路由基礎
下一篇
第 5 天:數據模型與遷移
系列文
後端小白自學 Laravel30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言